<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>

  <body>
    <script>
      /* 
        复习
          高阶函数：如果一个函数的参数是函数，或返回值是函数，则这个函数是高阶函数。

          变量的作用域：全局作用域和局部作用域
              1. 函数内部可以使用全局作用域
              2. 函数外部不可以使用局部作用域
              3. 当函数执行完毕，本地作用域的局部变量销毁
      */
      /* 
        基本数据类型存放的是值，
        复杂数据类型存放的是值的地址。
        obj 重新赋值后，内存地址指向了貂蝉，
        而吕布这个对象，系统判断没有被引用(不再被使用)，就会下次GC时会被自动垃圾回收。
        结论: 没有被引用的数据，将会被GC。
      */
      // hero 内存地址: 0x001234  
      let hero = {
        name: "吕布",
        sex: true,
      };
      // hero 内存地址: 0x005678 
      hero = {
        name: "貂蝉",
        sex: false,
      };

      /* 
        内存栈模型：stack
          1. 先进后出
          2. 里面存储的是函数
          3. add 函数的过程，称为 入栈
          4. delete函数，函数执行完毕，出栈。
      */
      /* 
        内存的堆模型：heap
           主要用来存储变量
      */ 
      function fn1(){
        fn2();
        console.log('fn1');
      }

      function fn2(){
        fn3();
        console.log('fn2');
      }

      function fn3(){
        console.log('fn3');
      }

      fn1();

      // 做一个简单的计数器
     
    

     

      // 现在我们已经达到了目的，可是问题来了，
      // 代码中的任何一个函数都可以随意改变counter的值，所以这个计数器并不完美。
      // 那我们把counter放在add函数里面不就好了么？

     
    

      // 所以这样做的话，每次调用add函数，counter的值都要被初始化为0，还是达不到我们的目的。

     

      /* 
        等同执行了下面的代码，add  的值是一个函数，因此可以调用。
        通过断点可以得知，将内部函数给了 add 。
        内部函数是复杂数据类型，传递值，有可以说 add 和 inner 指向的是同一个函数。
        而内部函数可以访问 out 的局部变量，即 add 可以访问 out 的局部变量。
        outer 函数执行后，销毁局部变量 counter 的时候，发现被 innner 使用，因此保存到内存中，不再销毁。

        var add = function innerFunction() {
            counter++;
            return counter;
        }
    
        结论: 在函数外部，可以函数内部的变量。原理：将局部变量放置内存中。
        注意: 容易内存泄漏，手动解除引用。
      */

   

      /* 
        闭包 closure 的定义 ：指有权访问另一个函数作用域中变量的函数
        一个作用域可以访问另一个函数内部的局部变量,局部变量所在函数就是闭包函数
        闭包 : 函数嵌套函数,内部函数可以引用外部函数的参数和变量,参数和变量不会被垃圾回收机制所收回

        闭包需要满足三个条件：
            1. 访问所在作用域；
            2. 函数嵌套；
            3. 在所在作用域外被调用

　　　　　　 优点：
            1. 可以读取函数内部的变量
　　　　　　　　  2. 可以避免全局污染
　　　　　　 缺点：
　　　　　　　　 1. 闭包会导致变量不会被垃圾回收机制所清除，会大量消耗内存；
　　　　　　　　 2. 不恰当的使用闭包可能会造成内存泄漏的问题；
        应用:
            一个是前面提到的可以读取函数内部的变量，另一个就是让这些变量的值始终保持在内存中。
      */

      // inner 方法可以访问 outer 方法中的局部变量 num，因此称 outer 是闭包函数
    </script>
  </body>
</html>